//https://raw.github.com/samizdatco/arbor/master/demos/halfviz/src/renderer.js
//http://blog.thinkst.com/2011/06/simple-graphs-with-arborjs.html
(function() {
	
	Renderer = function(canvas) {
		var canvas = $(canvas).get(0)
		var ctx = canvas.getContext("2d");
		var gfx = arbor.Graphics(canvas)
		var particleSystem = null

		var that = {
			init : function(system) {
				particleSystem = system
				particleSystem.screenSize(canvas.width, canvas.height)
				particleSystem.screenPadding(40)

				that.initMouseHandling()
			},
			
			redraw : function() {
				if (!particleSystem)
					return

				

				gfx.clear() // convenience ƒ: clears the whole canvas rect
				
				// draw the nodes & save their bounds for edge drawing
				var nodeBoxes = {}
				particleSystem.eachNode(function(node, pt) {
					// node: {mass:#, p:{x,y}, name:"", data:{}}
					// pt: {x:#, y:#} node position in screen coords
					
					// determine the box size and round off the coords if we'll
					// be
					// drawing a text label (awful alignment jitter
					// otherwise...)
					var label = node.data.label || ""
					var w = ctx.measureText("" + label).width + 10
					if (!("" + label).match(/^[ \t]*$/)) {
						pt.x = Math.floor(pt.x)
						pt.y = Math.floor(pt.y)
					} else {
						label = null
					}
					
					// draw a rectangle centered at pt
					if (node.data.color)
						ctx.fillStyle = node.data.color
					else
						ctx.fillStyle = "rgba(0,0,0,.2)"
					if (node.data.color == 'none')
						ctx.fillStyle = "white"

					if (node.data.shape == 'dot') {
						gfx.oval(pt.x - w / 2, pt.y - w / 2, w, w, {
							fill : ctx.fillStyle
						})
						nodeBoxes[node.name] = [ pt.x - w / 2, pt.y - w / 2, w, w ]
					} else {
						gfx.rect(pt.x - w / 2, pt.y - 10, w, 20, 4, {
							fill : ctx.fillStyle
						})
						nodeBoxes[node.name] = [ pt.x - w / 2, pt.y - 11, w, 22 ]
					}
					
					// draw the text
					if (label) {
						ctx.font = "12px Helvetica"
						ctx.textAlign = "center"
						ctx.fillStyle = "white"
						if (node.data.color == 'none')
							ctx.fillStyle = '#333333'
						ctx.fillText(label || "", pt.x, pt.y + 4)
						ctx.fillText(label || "", pt.x, pt.y + 4)
					}
				})

				// draw the edges
				particleSystem.eachEdge(function(edge, pt1, pt2) {
					// edge: {source:Node, target:Node, length:#, data:{}}
					// pt1: {x:#, y:#} source position in screen coords
					// pt2: {x:#, y:#} target position in screen coords
					
					var weight = edge.data.weight
					var color = edge.data.color

					if (!color || ("" + color).match(/^[ \t]*$/))
						color = null

						// find the start point
					var tail = intersect_line_box(pt1, pt2, nodeBoxes[edge.source.name])
					var head = intersect_line_box(tail, pt2, nodeBoxes[edge.target.name])

					ctx.save()
					ctx.beginPath()
					ctx.lineWidth = (!isNaN(weight)) ? parseFloat(weight) : 1
					ctx.strokeStyle = (color) ? color : "#cccccc"
					ctx.fillStyle = null

					ctx.moveTo(tail.x, tail.y)
					ctx.lineTo(head.x, head.y)
					ctx.stroke()
					ctx.restore()

					// draw an arrowhead if this is a -> style edge
					if (edge.data.directed) {
						ctx.save()
						// move to the head position of the edge we just drew
						var wt = !isNaN(weight) ? parseFloat(weight) : 1
						var arrowLength = 6 + wt
						var arrowWidth = 2 + wt
						ctx.fillStyle = (color) ? color : "#cccccc"
						ctx.translate(head.x, head.y);
						ctx.rotate(Math.atan2(head.y - tail.y, head.x - tail.x));
						
						// delete some of the edge that's already there (so the
						// point isn't hidden)
						ctx.clearRect(-arrowLength / 2, -wt / 2, arrowLength / 2, wt)

						// draw the chevron
						ctx.beginPath();
						ctx.moveTo(-arrowLength, arrowWidth);
						ctx.lineTo(0, 0);
						ctx.lineTo(-arrowLength, -arrowWidth);
						ctx.lineTo(-arrowLength * 0.8, -0);
						ctx.closePath();
						ctx.fill();
						ctx.restore()
					}
				})

			},
			initMouseHandling : function() {
				// no-nonsense drag and drop (thanks springy.js)
				selected = null;
				nearest = null;
				var dragged = null;
				var oldmass = 1

				// set up a handler object that will initially listen for
				// mousedowns then
				// for moves and mouseups while dragging
				var handler = {
					clicked : function(e) {
						var pos = $(canvas).offset();
						_mouseP = arbor.Point(e.pageX - pos.left, e.pageY - pos.top)
						selected = nearest = dragged = particleSystem.nearest(_mouseP);
						
						if (dragged.node !== null)
							dragged.node.fixed = true

						$(canvas).bind('mousemove', handler.dragged)
						$(window).bind('mouseup', handler.dropped)

						return false
					},
					dragged : function(e) {
						var old_nearest = nearest && nearest.node._id
						var pos = $(canvas).offset();
						var s = arbor.Point(e.pageX - pos.left, e.pageY - pos.top)

						if (!nearest)
							return

						if (dragged !== null && dragged.node !== null) {
							var p = particleSystem.fromScreen(s)
							dragged.node.p = p
						}
						
						return false
					},
					
					dropped : function(e) {
						if (dragged === null || dragged.node === undefined)
							return

						if (dragged.node !== null)
							dragged.node.fixed = false
						dragged.node.tempMass = 50
						dragged = null
						selected = null
						$(canvas).unbind('mousemove', handler.dragged)
						$(window).unbind('mouseup', handler.dropped)
						_mouseP = null
						return false
					}
				}
				$(canvas).mousedown(handler.clicked);
				
			}
		
		}

		// helpers for figuring out where to draw arrows (thanks springy.js)
		var intersect_line_line = function(p1, p2, p3, p4) {
			var denom = ((p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y));
			if (denom === 0)
				return false // lines are parallel
			var ua = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denom;
			var ub = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denom;
			
			if (ua < 0 || ua > 1 || ub < 0 || ub > 1)
				return false
			return arbor.Point(p1.x + ua * (p2.x - p1.x), p1.y + ua * (p2.y - p1.y));
		}

		var intersect_line_box = function(p1, p2, boxTuple) {
			var p3 = {
				x : boxTuple[0],
				y : boxTuple[1]
			}, w = boxTuple[2], h = boxTuple[3]

			var tl = {
				x : p3.x,
				y : p3.y
			};
			var tr = {
				x : p3.x + w,
				y : p3.y
			};
			var bl = {
				x : p3.x,
				y : p3.y + h
			};
			var br = {
				x : p3.x + w,
				y : p3.y + h
			};
			
			return intersect_line_line(p1, p2, tl, tr) || intersect_line_line(p1, p2, tr, br) || intersect_line_line(p1, p2, br, bl) || intersect_line_line(p1, p2, bl, tl) || false
		}

		return that
	}

})()